package cn.nkpro.elcube.docengine.cloud;

import cn.nkpro.elcube.basic.PageList;
import cn.nkpro.elcube.cloud.NkAbstractCloudService;
import cn.nkpro.elcube.cloud.NkCloudSyncEvent;
import cn.nkpro.elcube.docengine.NkDocEngine;
import cn.nkpro.elcube.docengine.NkDocEngineThreadLocal;
import cn.nkpro.elcube.docengine.gen.DocH;
import cn.nkpro.elcube.docengine.model.DocHV;
import cn.nkpro.elcube.docengine.model.DocState;
import cn.nkpro.elcube.docengine.service.NkDocEngineFrontService;
import cn.nkpro.elcube.docengine.service.impl.NkDocFinder;
import cn.nkpro.elcube.exception.NkOperateNotAllowedException;
import cn.nkpro.elcube.security.NkSecurityRunner;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;

/**
 * 单据引擎代理
 */
public class NkCloudDocEngine extends NkAbstractCloudService<NkDocEngineFrontService> implements NkDocEngineFrontService, ApplicationListener<NkCloudSyncEvent> {


    @Autowired
    private NkSecurityRunner securityRunner;
    @Autowired(required = false)
    protected NkCloudDocEngineLock docEngineLock;

    @Override
    public PageList<DocH> list(String docType, int offset, int rows, String orderBy) {
        return switchTarget(docType).list(clearSymbol(docType), offset, rows, orderBy);
    }

    @Override
    public DocHV createForView(String docType, String preDocId) {
        return switchTarget(docType).createForView(clearSymbol(docType), preDocId);
    }

    @Override
    public DocHV copyForView(String copyFromId) {
        return switchTarget(copyFromId).copyForView(copyFromId);
    }

    @Override
    public DocHV calculateForView(DocHV doc, String fromCard, Object options) {
        return switchTarget(doc.getDocId()).calculateForView(doc,fromCard,options);
    }

    @Override
    public Object call(DocHV doc, String fromCard, String method, Object options) {
        return switchTarget(doc.getDocId()).call(doc,fromCard,method,options);
    }

    @Override
    public DocState state(String docId) {
        return switchTarget(docId).state(docId);
    }

    @Override
    public DocHV execSyncUpdate(DocHV doc, String optSource, String forceIdentification) {
        throw new RuntimeException("不允许的操作");
    }

    @Override
    public DocHV doUpdateView(DocHV doc, String optSource, UpdateConfig config) {
        return switchTarget(doc.getDocId()).doUpdateView(doc,optSource, config);
    }

    @Override
    public DocHV detailView(String docId, boolean edit) {
        return switchTarget(docId).detailView(docId, edit);
    }


    /**
     * 处理访问远程服务时的缓存
     * 因为一个请求中，对detail的访问频率会非常高
     */
    @Override
    public DocHV detail(String docId) {

        NkDocEngine local = local();
        NkDocEngine target = switchTarget(docId);

        if(local==target){
            return local.detail(docId);
        }else{
            return NkDocEngineThreadLocal.localDoc(docId, target::detail);
        }
    }

    @Override
    public DocHV detail(String docType, String businessKey) {
        return switchTarget(docType).detail(clearSymbol(docType),businessKey);
    }

    // 以下方法不支持远程访问

    @Override
    public void onBpmKilled(String docId, String processKey, String optSource) {
        throwErrorIfRemote(docId);
        local().onBpmKilled(docId,processKey,optSource);
    }

    @Override
    public void reDataSync(DocHV doc) {
        throwErrorIfRemote(doc.getDocId());
        local().reDataSync(doc);
    }

    @Override
    public NkDocFinder find(String... docType) {
        for(String t : docType)throwErrorIfRemote(t);
        return local().find(docType);
    }

    @Override
    public DocHV create(String docType, String preDocId, String optSource, Function function) {
        throwErrorIfRemote(preDocId);
        return local().create(docType,preDocId,optSource,function);
    }

    @Override
    public DocHV createIfYes(String docType, String preDocId, String optSource, FunctionCancelable function) {
        throwErrorIfRemote(preDocId);
        return local().createIfYes(docType,preDocId,optSource,function);
    }

    @Override
    public DocHV calculate(DocHV doc, String fromCard, Object options) {
        throwErrorIfRemote(doc.getDocId());
        return local().calculate(doc,fromCard,options);
    }

    @Override
    public DocHV doUpdate(String docId, String optSource, Function function) {
        throwErrorIfRemote(docId);
        return local().doUpdate(docId,optSource,function);
    }

    @Override
    public DocHV doUpdate(String docType, String businessKey, String optSource, Function function) {
        throwErrorIfRemote(docType);
        return local().doUpdate(docType,businessKey,optSource,function);
    }

    @Override
    public DocHV doUpdateAgain(String docId, String optSource, Function function) {
        throwErrorIfRemote(docId);
        return local().doUpdateAgain(docId,optSource,function);
    }

    @Override
    public DocHV random(DocHV doc) {
        throwErrorIfRemote(doc.getDocId());
        return local().random(doc);
    }

    @Override
    protected Class<NkDocEngineFrontService> targetType() {
        return NkDocEngineFrontService.class;
    }

    @Override
    public void onApplicationEvent(NkCloudSyncEvent event) {
        if(!StringUtils.equals(event.getProducerId(),cloudProperties.getId())){
            switch (event.getType()){
                case "doc":{
                    DocHV doc = ((JSONObject)event.getObject()).toJavaObject(DocHV.class);
                    NkCloudDocEngineSyncHelper.run(()->{
                        securityRunner.returnAsUser("admin", () ->
                                local().execSyncUpdate(doc, "云同步", doc.getIdentification())
                        );
                    });
                    break;
                }
                case "docId":{
                    DocHV doc = remote(event.getProducerId()).detail((String) event.getObject());
                    NkCloudDocEngineSyncHelper.run(()->{
                        securityRunner.returnAsUser("admin", () ->
                                local().execSyncUpdate(doc, "云同步", doc.getIdentification())
                        );
                    });
                    break;
                }
                default:
                    throw new NkOperateNotAllowedException("未知的同步事件类型:"+event.getType());
            }
        }
    }
}
